﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Cosmos.Assembler.x86;
using CORE = Cosmos.Core;
using UI = DimOS.DimSH.Interface;
using Cosmos.Core.IOGroup;
using DimOS.Constants;
using DimOS.Internals;

namespace DimOS.VTS
{
    public class Base
    {
        private static byte _color = 0x0F;
        private static UInt16 _nullCharacter;
        private static readonly UInt16 OutputColumns = (UInt16)(TotalColumns - UI.InfoColumns);
        private static readonly UInt32 Address = (UInt32)(TotalColumns * (TotalRows - 3) * 2);
        private static readonly UInt32 ScrollSize = (UInt32)(OutputColumns * 2); //1 Row
        private static readonly UInt16 OutputClearSize = (UInt16)(OutputColumns / 2); //1 Row
        private static readonly UInt16 FullClearSize = (UInt16)(TotalColumns / 2); //1 Row
        private static readonly int LastRow = TotalRows - 3;

        // ReSharper disable once InconsistentNaming
        private static readonly TextScreen IO = new TextScreen(); //TextScreen IO
        protected static UInt32 BufferSize = IO.Memory.Size;
        protected static UInt32 BufferBase = IO.Memory.Base;

        public static UInt16 TotalRows { get { return 25; } /*Setter cannot be implemented yet*/}
        public static UInt16 TotalColumns { get { return 80; } /*Setter cannot be implemented yet*/}

        private static ConsoleColor _foreground = Colors.SectionInputForeground;
        public static ConsoleColor GetForeground() { return _foreground; }
        public static void SetForeground(ConsoleColor value) { _foreground = value;
            _color = (byte)(((byte)(value) & 0x0F) | ((byte)(_background) << 4));
            _nullCharacter = (UInt16)(_color << 8 | 0x00); }
        public static void SetForeground(int x, int y, ConsoleColor value)
        {
            IO.Memory.Bytes[(UInt32)((x + y * TotalColumns) * 2) + 1] =
                (byte)((byte)(value) | ((byte)(_background) << 4));
        }

        private static ConsoleColor _background = Colors.SectionInputBackground;
        public static ConsoleColor GetBackground() { return _background; }
        public static void SetBackground(ConsoleColor value) { _background = value;
            _color = (byte)(((byte)(_foreground) & 0x0F) | ((byte)(value) << 4));
            _nullCharacter = (UInt16)(_color << 8 | 0x00); }
        public static void SetBackground(int x, int y, ConsoleColor value)
        {
            IO.Memory.Bytes[(UInt32)((x + y * TotalColumns) * 2) + 1] =
                (byte)((byte)(_foreground) | ((byte)(value) << 4));
        }

        private static Point _position = new Point(0, TotalRows - 1);
        public static Point GetPosition() { return _position; }
        public static void SetPosition(Point value)
        { 
            _position = value;

            char pos = (char)((_position.Y * TotalColumns) + _position.X);
            IO.Idx3.Byte = 0x0F; IO.Data3.Byte = (byte)(pos & 0xFF);
            IO.Idx3.Byte = 0x0E; IO.Data3.Byte = (byte)(pos >> 8);
        }

        public static int PosX { get { return _position.X; } set { _position.X = value; SetPosition(_position); } }
        public static int PosY { get { return _position.Y; } set { _position.Y = value; SetPosition(_position); } }

        private static int _cursorSize = 20;
        private static int _trueCursorSize = 12;
        public static int GetCursorSize() { return _cursorSize; }
        public static void SetCursorSize(int value)
        {
            _cursorSize = value;
            value = 16 - ((16 * value) / 100);
            if (value >= 16) value = 15;
            _trueCursorSize = value;

            IO.Idx3.Byte = 0x0A; IO.Data3.Byte = (byte)value;
            IO.Idx3.Byte = 0x0B; IO.Data3.Byte = 0x0F;
        }

        public static void HideCursor()
        {
            IO.Idx3.Byte = 0x0A;
            IO.Data3.Byte |= 1 << 5;
        }

        public static void ShowCursor()
        {
            IO.Idx3.Byte = 0x0A;
            IO.Data3.Byte = (byte)_trueCursorSize;
        }

        public static void ClearOutput()
        {
            for (int i = 0; i < (TotalRows - 2) * 2; i += 2)
                IO.Memory.Fill((UInt32)(TotalColumns * i), OutputClearSize, _nullCharacter);
        }

        public static void ClearFull()
        {
            for (int i = 0; i < TotalRows * 2; i += 2)
                IO.Memory.Fill((UInt32)(TotalColumns * i), FullClearSize, _nullCharacter);
        }

        public static void ScrollForward(int offset = 0)
        {
            for (int i = offset * 2; i < LastRow * 2; i += 2)
                IO.Memory.MoveDown((UInt32)(TotalColumns * i),
                    (UInt32)(TotalColumns * (i + 2)), ScrollSize);
            IO.Memory.Fill(Address, OutputClearSize, _nullCharacter);
        }

        public static void ScrollBackward()
        {
            for (int i = LastRow * 2 - 2; i >= 0; i -= 2)
                IO.Memory.MoveDown((UInt32)(TotalColumns * (i + 2)),
                    (UInt32)(TotalColumns * i), ScrollSize);
            IO.Memory.Fill(0, OutputClearSize, _nullCharacter);
        }

        public static byte GetColor(int x, int y)
        { return IO.Memory.Bytes[(UInt32)(((x + y * TotalColumns) * 2) + 1)]; }

        public static byte GetColor() { return _color; }

        public static char GetCharacter(int x, int y)
        { return (char)IO.Memory.Bytes[(UInt32)((x + y * TotalColumns) * 2)]; }

        public static void SetCharacter(int x, int y, char value)
        {
            var address = (UInt32)((x + y * TotalColumns) * 2);
            IO.Memory.Bytes[address] = (byte)value;
            IO.Memory.Bytes[address + 1] = _color;
        }

        public static void SetCharacter(int x, int y, char value, byte color)
        {
            var address = (UInt32)((x + y * TotalColumns) * 2);
            IO.Memory.Bytes[address] = (byte)value;
            IO.Memory.Bytes[address + 1] = color;
        }

        public static void SetCharacter(int x, int y, char value, ConsoleColor foreground, ConsoleColor background)
        {
            var address = (UInt32)((x + y * TotalColumns) * 2);
            IO.Memory.Bytes[address] = (byte)value;
            IO.Memory.Bytes[address + 1] = (byte)(((byte)(foreground) & 0x0F) | ((byte)(background) << 4));
        }
    }
}
